/* $Id:import.cc $ */
/*
   Copyright (C) 2007 by Jurjen Stellingwerff <jurjen@stwerff.xs4all.nl>
   Part of the Moros Project http://moros.sourceforge.net/

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License version 3.
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY.

   See the COPYING file for more details.
*/

// TODO (jurjen#1#) think of a method to read actions between skills

#define DEBUG_ROUTINE "import"

#include <unistd.h>
#include <iostream>
#include "log.h"
#include "channel.h"
#include "import.h"
#include "general.h"
#include "util.h"

using namespace std;

/// the current character of the file
static char c=0;
/// set when this line seems to be a description
static bool descr=false;
/// set when the current character is directly after a new line, a start of a new file is also a new line
static bool newl=true;
/// set when reading a value
static bool val=false;
/// set when reading a key
static bool key=false;
/// keeps the current start of the line primarily for getting the record name from a line when it was still unknown if it could be a record line
static string cur="";
/// current file pointer of the file to read
static FILE *fp=NULL;
/// set when the current line appears to be a record line
static bool record=false;
static string crec="";
list<pair<string,string> > todo;

/// list of possible record names
string records[] = {"rule", "paragraph", "chapter", "category", "job", "background", "explain", "deity", "action", "skill", "special", "guild", "creature", "unit", "item", "race", "guild", "culture", "unit", "aging"};


/// open a new file for reading
void set_file(string file) {
    dbg_state("reading file: %s", file.c_str());
    if (fp!=NULL) fclose(fp);
    fp=fopen(file.c_str(), "r");
    c=0;
}

/// get the next character from the file
void next() {
    int chr=getc(fp);
    if (chr != EOF) c=chr;
    else c=0;
}

bool firstpair(channel *ch) {
    for(list< pair<string, string> > :: iterator i = todo.begin(); i != todo.end(); i++) {
        ch->key=i->first;
        ch->value=i->second;
        dbg_state("read %s: '%s' pair", ch->key.c_str(), ch->value.c_str());
        todo.erase(i);
        return true;
    }
    return false;
}

void keypair(channel *ch) {
    if (crec=="action" && ch->key=="time") {
        if (ch->value=="hour" || ch->value=="day") {
            todo.push_back(pair<string,string>("time", "1"));
            todo.push_back(pair<string,string>("time_base", ch->value));
        } else {
            string part=split(&ch->value, " ");
            if (ch->value=="") ch->value="second";
            todo.push_back(pair<string,string>("time", part));
            todo.push_back(pair<string,string>("time_base", ch->value));
        }
    } else if ((crec=="category" || crec=="race") && (ch->key=="constitution" || ch->key=="willpower" || ch->key=="movement" || ch->key=="reach")) {
        todo.push_back(pair<string,string>("background_level",""));
        todo.push_back(pair<string,string>("background",ch->key));
        todo.push_back(pair<string,string>("level",ch->value));
    } else if ((crec=="category" || crec=="race") && ch->key=="background") {
        string part=split(&ch->value, ", ");
        while(part!="") {
            string background=split(&part, " ");
            string level=part;
            todo.push_back(pair<string,string>("background_level",""));
            todo.push_back(pair<string,string>("background", background));
            todo.push_back(pair<string,string>("level", level));
            part=split(&ch->value, ", ");
        }
    } else if (crec=="item" && ch->key=="spell") {
        string part=split(&ch->value, ", ");
        while(part!="") {
            string spell=split(&part, " ");
            string level=part;
            todo.push_back(pair<string,string>("item_spell",""));
            todo.push_back(pair<string,string>("spell", spell));
            todo.push_back(pair<string,string>("level", level));
            part=split(&ch->value, ", ");
        }
    } else if (crec=="unit" && ch->key=="culture") {
        string part=split(&ch->value, ", ");
        while(part!="") {
            todo.push_back(pair<string,string>("culture_part",""));
            todo.push_back(pair<string,string>("culture", part));
            part=split(&ch->value, ", ");
        }
    } else if ((crec=="person" || crec=="unit" || crec=="race" || crec=="guild") && (ch->key=="skill" || ch->key=="skills")) {
        string part=split(&ch->value, ", ");
        while(part!="") {
            string skill=split(&part, " ");
            string level=part;
            todo.push_back(pair<string,string>("skill_level",""));
            todo.push_back(pair<string,string>("skill", skill));
            todo.push_back(pair<string,string>("level", level));
            part=split(&ch->value, ", ");
        }
    } else if (crec=="item" && ch->key=="usage") {
        string part=split(&ch->value, " | ");
        while(part!="") {
            string action=split(&part, " ");
            string level=part;
            todo.push_back(pair<string,string>("usage",""));
            todo.push_back(pair<string,string>("action", action));
            todo.push_back(pair<string,string>("level", level));
            part=split(&ch->value, ", ");
        }
    } else if ((crec=="unit" || crec=="race") && ch->key=="special") {
        string part=split(&ch->value, ", ");
        while(part!="") {
            string action=split(&part, " ");
            string level=part;
            todo.push_back(pair<string,string>("action_level",""));
            todo.push_back(pair<string,string>("action", action));
            todo.push_back(pair<string,string>("level", level));
            part=split(&ch->value, ", ");
        }
    } else if ((crec=="action" && ch->key=="skill") || (crec=="item" && ch->key=="creation")) {
        string part=split(&ch->value, " | ");
        while(part!="") {
            string skill1=split(&part, ", ");
            string skill2=part;
            size_t pos=skill1.find_last_not_of(" -0123456789");
            string level="";
            dbg_state("skill `%s` pos %i", skill1.c_str(), pos);
            if (pos<skill1.length()-1 && pos>0) {
                level=skill1.substr(pos+2);
                skill1=skill1.substr(0,pos+1);
            }
            pos=skill2.find_last_not_of(" -0123456789");
            dbg_state("skill `%s` pos %i", skill2.c_str(), pos);
            if (pos<skill2.length()-1 && pos>0) {
                if (level=="" || level==" ") {
                    level=skill2.substr(pos+2);
                }
                skill2=skill2.substr(0,pos+1);
            }
            todo.push_back(pair<string,string>("action_skill",""));
            todo.push_back(pair<string,string>("first_skill", skill1));
            if (skill2!="") todo.push_back(pair<string,string>("second_skill", skill2));
            todo.push_back(pair<string,string>("level", level));
            part=split(&ch->value, " | ");
        }
    } else if (crec=="action" && ch->key=="bonus") {
        string part=split(&ch->value, " | ");
        while(part!="") {
            unsigned int pos=part.find_first_not_of(" 0123456789");
            string points="";
            if (pos<part.length() && pos>0) {
                points=part.substr(0,pos-1);
                part=part.substr(pos);
            }
            for (unsigned int t=0; t<part.length(); t++) {
                if (part[t]==' ') part[t]='_';
            }
            todo.push_back(pair<string,string>("bonus",""));
            todo.push_back(pair<string,string>("points", points));
            todo.push_back(pair<string,string>("type", part));
            part=split(&ch->value, " | ");
        }
    } else if (crec=="item" && ch->key=="bonus") {
        string part=split(&ch->value, ", ");
        while(part!="") {
            string bonus=split(&part, " ");
            todo.push_back(pair<string,string>("bonus",""));
            if (part!="") todo.push_back(pair<string,string>("points", part));
            todo.push_back(pair<string,string>("type", bonus));
            part=split(&ch->value, ", ");
        }
    } else if (crec=="race" && ch->key=="age") {
        string part=split(&ch->value, " ");
        string age=split(&part, "%");
        todo.push_back(pair<string,string>("age_percentage",age));
        if (ch->value!="") {
            todo.push_back(pair<string,string>("aging",ch->value));
        }
    } else if ((crec=="race" || crec=="unit") && ch->key=="weapons") {
        string part=split(&ch->value, ", ");
        while(part!="") {
            string background=split(&part, " ");
            string level=part;
            todo.push_back(pair<string,string>("weapons",""));
            todo.push_back(pair<string,string>("item", background));
            if (level!="") todo.push_back(pair<string,string>("position", level));
            part=split(&ch->value, ", ");
        }
    } else if (crec=="item" && ch->key=="material") {
        string part=split(&ch->value, ", ");
        while(part!="") {
            string item=split(&part, " ");
            string level=part;
            todo.push_back(pair<string,string>("material",""));
            todo.push_back(pair<string,string>("item", item));
            todo.push_back(pair<string,string>("amount", level));
            part=split(&ch->value, ", ");
        }
    } else if (crec=="job" && ch->key=="special") {
        string part=split(&ch->value, ", ");
        while(part!="") {
            todo.push_back(pair<string,string>("special",""));
            todo.push_back(pair<string,string>("item", part));
            todo.push_back(pair<string,string>("amount", "1"));
            part=split(&ch->value, ", ");
        }
    } else {
        todo.push_back(pair<string,string>(ch->key,ch->value));
    }
    firstpair(ch);
}

/// find the next key-value pair in the file
bool import_next(channel *ch) {
    if (firstpair(ch)) return true;
    if (record) { // this line is a new record line
        cur="";
        record=false;
        ch->key="name";
        ch->value="";
        val=true;
    } else {
        ch->key="";
        ch->value="";
        descr=false;
        val=false;
        key=false;
    }
    if (c==0) next();
    while (c!=0) {
        if (c=='\n') {
            next();
            newl=true;
            if (!val) {
                if (ch->value=="") ch->value=cur;
                else ch->value+="\n"+cur;
                descr=true;
            }
            cur="";
            if (!descr) {
                keypair(ch);
                return true;
            }
        } else if (c=='.' && newl) {
            next();
            if (c!=' ') {
                if (descr) ch->value+="\n.";
                else {
                    descr=true;
                    ch->value=".";
                    cur=".";
                }
            } else {
                if (descr) {
                    while (ch->value != "" && ch->value.substr(ch->value.length()-1)=="\n") ch->value.substr(0,ch->value.length()-1);
                    todo.push_back(pair<string,string>("description",ch->value));
                    ch->value="";
                    descr=false;
                }
                while(c==' ') next();
                key=true;
            }
        } else if (c==':') {
            next();
            if (!key) {
              dbg_state("checking for record '%s'", cur.c_str());
              loopArray(records,rec,string) if (*rec==cur) { // scan for record names
                if (c==' ') next();
                crec=cur;
                if (descr) { // send the current description first
                    if (ch->value.length() >= cur.length() && ch->value.substr(ch->value.length()-cur.length())==cur) {
                        ch->value=ch->value.substr(0,ch->value.length()-cur.length());
                    }
                    while (ch->value!="" && ch->value.substr(ch->value.length()-1)=="\n") ch->value=ch->value.substr(0,ch->value.length()-1);
                    descr=false;
                    if (ch->value!="") todo.push_back(pair<string,string>("description",ch->value));
                }
                record=true;
                if (cur=="chapter" || cur=="paragraph") {
                    todo.push_back(pair<string,string>("rule", ""));
                    todo.push_back(pair<string,string>("link", cur));
                } else if (cur=="background" || cur=="race" || cur=="jobs" || cur=="culture" || cur=="deity" || cur=="skill" || cur=="item" || cur=="aging" || cur=="gender" || cur=="action") {
                    todo.push_back(pair<string,string>("rule", ""));
                    todo.push_back(pair<string,string>("link", cur));
                    string name="";
                    while (c!='\n' && c!=0) { // problem.. the name in on the current line but not here... scan first and the write also the name rule
                        name+=c;
                        next();
                    }
                    next();
                    todo.push_back(pair<string,string>("name", name));
                    todo.push_back(pair<string,string>(cur, ""));
                    todo.push_back(pair<string,string>("name", name));
                    cur="";
                    val=true;
                    descr=false;
                    record=false;
                } else todo.push_back(pair<string,string>(cur, ""));
                firstpair(ch);
                return true;
              }
            }
            if (!key) {
                descr=true;
                cur+=":";
            } else {
                if (c==' ') next();
                cur="";
                key=false;
                val=true;
            }
        } else if (c==13) next();
        else {
            newl=false;
            if (key) ch->key+=c;
            else if (val) ch->value+=c;
            cur+=c;
            next();
        }
    }
    fclose(fp);
    fp=NULL;
    dbg_state("read %s: '%s' eof", ch->key.c_str(), ch->value.c_str());
    return true;
}
